package org.harsh.dispatch;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import org.harsh.common.net.PingArguments;
import org.harsh.common.net.PingResult;
import org.harsh.common.net.PingUtil;
import org.harsh.common.NodeStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.xmlunit.util.Nodes;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Component("nodeStatusCheck")
public class NodeStatusCheckTask {
    public static Map<Long, NodeStatus> nodeStatusMap = new ConcurrentHashMap<>();
    @Autowired
    private ISysDeptService sysDeptService;

    public void updateNodeMap() {
        List<SysDept> deptList = sysDeptService.selectOtherDeptList();
        for (SysDept sysDept : deptList) {
            if (StrUtil.isEmpty(sysDept.getLocalAddr())) {
                continue;
            }
            if (nodeStatusMap.containsKey(sysDept.getDeptId())) {
                continue;
            }
            nodeStatusMap.put(sysDept.getDeptId(),
                    new NodeStatus(sysDept.getDeptId(), sysDept.getDeptName(), sysDept.getLocalAddr())
            );
        }
        // todo 后期修复
//        for(long nodeId : nodeStatusMap.keySet()) {
//            // fixed 对于变更的NodeId, 清除网络状态监测
//            if(!deptList.contains(nodeId)) {
//                nodeStatusMap.remove(nodeId);
//            }
//        }
    }

    /**
     * 定期进行节点状态检测
     */
    public void run() {
        updateNodeMap();

        log.info("【检测节点连通情况】---------");
        for (Long deptId : nodeStatusMap.keySet()) {
            NodeStatus nodeStatus = nodeStatusMap.get(deptId);
            CommVars.executorService.submit(new Runnable() {
                private PingUtil.Backend getBackend() {
                    Properties properties = System.getProperties();
                    String osName = properties.getProperty("os.name");
                    if (StrUtil.containsIgnoreCase(osName, "Windows")) {
                        return PingUtil.Backend.WINDOWS_zhCN;
                    } else {
                        return PingUtil.Backend.UNIX;
                    }
                }

                @Override
                public void run() {
                    PingArguments.Builder builder = new PingArguments.Builder();
                    PingArguments args = builder.url(nodeStatus.getNodeAddr()).count(5).build();

                    PingResult result = null;
                    try {
                        result = PingUtil.ping(args, getBackend());
                        if (result != null && result.getTtl() != -1) {
                            nodeStatus.setConnectivity(true);
                            nodeStatus.setTtl((int) result.getTtl());
                            nodeStatus.setMinDelay((int) result.getRtt_min());
                            nodeStatus.setMaxDelay((int) result.getRtt_max());
                            nodeStatus.setDelay((int) result.getRtt_avg());
                            nodeStatus.setLossRate((int) result.getLossRate());
                            nodeStatus.setCheckPoint(DateUtil.now());
                            log.info("【节点连通性检查】IP地址：{}，连通正常，往返时间 {} ms---------", nodeStatus.getNodeAddr(), nodeStatus.getDelay());
                        } else {
                            nodeStatus.setConnectivity(false);
                            nodeStatus.setDelay(-1);
                            log.info("【节点连通性检查】IP地址：{}，连通异常---------", nodeStatus.getNodeAddr());
                        }
                    } catch (Exception e) {
                        nodeStatus.setConnectivity(false);
                        nodeStatus.setDelay(-1);
                        log.info("【节点连通性检查】IP地址：{}，连通异常---------", nodeStatus.getNodeAddr());
                    }
                }
            });
        }
    }

    public static int getDelay(long nodeId) {
        if (nodeStatusMap.containsKey(nodeId)) {
            return nodeStatusMap.get(nodeId).getDelay();
        }
        return -1;
    }

    /**
     * 检查节点是否可达
     * @param nodeId
     * @return
     */
    public static boolean checkConnectivity(long nodeId) {
        NodeStatus nodeStatus =  nodeStatusMap.getOrDefault(nodeId, null);
        return nodeStatus==null ? false : nodeStatus.isConnectivity();
    }

    /**
     * 获取所有可连接的节点
     * @return
     */
    public static List<Long> getConnectivity() {
        List<Long> connectivityNode = new ArrayList<>();
        for (long id : nodeStatusMap.keySet()) {
            if (nodeStatusMap.get(id).isConnectivity()) {
                connectivityNode.add(id);
            }
        }
        return connectivityNode;
    }

    /**
     * 判断是否采用TCP连接
     * @param nodeId
     * @return
     */
    public static boolean canUseTcp(long nodeId) {
        NodeStatus nodeStatus = nodeStatusMap.get(nodeId);
        // 判断使用TCP的约束条件
        if (nodeStatus.isConnectivity() && ((nodeStatus.getLossRate() < 5) || (nodeStatus.getDelay() > 0 && nodeStatus.getDelay() < 10) &&
                (nodeStatus.getMaxDelay() - nodeStatus.getMinDelay() < 10)))
        {
            return true;
        }
        return false;
    }
}
